/**
 * Libevent例程：实现基于事件的服务器
 * @author Martin
 * @date 2021-8-10
 * @note 与实现基于事件的客户端（Event_Client.c）搭配使用
 * 编译：
 * $ gcc -o eventserver Event_Server.c -levent
 */

#include <sys/stat.h>
#include <event.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

/**
 * 从fd读取数据
 * @param fd 要读取的文件描述符
 * @param event 就绪的事件类型
 * @param arg 用户参数
 */
static void
fifo_read(evutil_socket_t fd, short event, void *arg)
{
    char buf[255];
    ssize_t len;
    struct event *ev = arg;

    /* 重新安排该事件 */
    event_add(ev, NULL); /* 如果不重新安排，事件处理完毕后，不会再触发 */

    fprintf(stdout, "fifo_read called with fd: %d, event: %d, arg: %p\n", (int)fd, event, arg);

    len = read(fd, buf, sizeof(buf) - 1);
    if (len == -1) {
        perror("read error");
        return;
    }
    else if (len == 0) {
        fprintf(stderr, "Connection closed\n");
        return;
    }
    buf[len] = '\0';

    fprintf(stdout, "Read: %s\n", buf);
}

int main(int argc, char *argv[])
{
    struct event evfifo;
    struct stat st;
    const char *fifo = "event.fifo";
    int socket;

    if (lstat(fifo, &st) == 0) {
        if ((st.st_mode & S_IFMT) == S_IFREG) { /* regular file */
            errno = EEXIST; /* 文件已经存在 */
            perror("lstat error");
            exit(1);
        }
    }

    /* 创建有名管道文件（FIFO文件） */
    unlink(fifo);
    if (mkfifo(fifo, 0600) == -1) {
        perror("mkfifo error");
        exit(1);
    }

    /* 如果管道文件已损坏，那么我们需要以O_RDWR方式打开，而不是O_RDONLY */
    socket = open(fifo, O_RDONLY | O_NONBLOCK, 0);
    if (socket == -1) {
        perror("open error");
        exit(1);
    }

    fprintf(stdout, "Write data to %s\n", fifo);

    /* 初始化event lib */
    event_init(); /* 多线程不安全，已启用，被event_base_new替代*/

    event_set(&evfifo, socket, EV_READ, fifo_read, &evfifo); /* 创建事件 */
    event_add(&evfifo, NULL); /* 添加到active events，未设置超时值 */

    event_dispatch();

    return 0;
}
